package ro.epb.menubegone.xposed; import java.util.Set; import java.util.TreeSet; import ro.epb.menubegone.core.Constants; import ro.epb.menubegone.core.Logger; import android.hardware.input.InputManager; import android.os.SystemClock; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.IXposedHookZygoteInit; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XSharedPreferences; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; public class Main implements IXposedHookLoadPackage, IXposedHookZygoteInit { private Set<String> blacklist; private boolean remapLong; @Override public void initZygote(StartupParam startupParam) throws Throwable { XSharedPreferences prefs = new XSharedPreferences(Constants.PACKAGE, Constants.PREF_FILE); blacklist = prefs.getStringSet(Constants.PREF_BLACKLIST, new TreeSet<String>()); remapLong = prefs.getBoolean(Constants.PREF_LONG_REMAP, true); if (blacklist.isEmpty()) Logger.Log("blacklist is empty"); else Logger.Log("Blacklist start:"); for (String string : blacklist) { Logger.Log(string); } } @Override public void handleLoadPackage(LoadPackageParam packageParam) throws Throwable { if (!blacklist.contains(packageParam.packageName)) { Logger.Log("run"); hookAppProcess(packageParam); } else { Logger.Log("ignore " + packageParam.packageName); } if (packageParam.packageName.equals("android")) hookAndroidProcess(packageParam); } private void hookAppProcess(LoadPackageParam packageParam) { XposedHelpers.findAndHookMethod("android.view.ViewConfiguration", packageParam.classLoader, "hasPermanentMenuKey", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { param.setResult(Boolean.valueOf(false)); } }); } private void hookAndroidProcess(LoadPackageParam packageParam) { Logger.Log("Loaded sys app(package: android)"); // this is how you hook overrides Class<?> PhoneWindowManager = XposedHelpers.findClass( "com.android.internal.policy.impl.PhoneWindowManager", packageParam.classLoader); XposedBridge.hookAllMethods(PhoneWindowManager, "interceptKeyBeforeDispatching", new XC_MethodHook() { boolean oldDown = false; @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { KeyEvent event = (KeyEvent) param.args[1]; if (event.getKeyCode() != KeyEvent.KEYCODE_MENU) return; boolean virtualKey = event.getDeviceId() == KeyCharacterMap.VIRTUAL_KEYBOARD; if (virtualKey) { param.setResult(Long.valueOf(0)); return; } boolean longPress = (event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0; boolean down = event.getAction() == KeyEvent.ACTION_DOWN; // Logger.Log("long " + longPress); // Logger.Log("down " + down); if (remapLong) { if (down) { if (longPress) { oldDown = false; injectKey(KeyEvent.KEYCODE_MENU); } else { oldDown = true; } } else if (oldDown) { injectKey(KeyEvent.KEYCODE_APP_SWITCH); oldDown = false; } } else { if (!oldDown && down && !longPress){ injectKey(KeyEvent.KEYCODE_APP_SWITCH); oldDown = true; } if(!down) oldDown = false; } param.setResult(Long.valueOf(-1)); // return value 0 makes the dispatcher share the key // with an app // return value -1 makes the dispatcher share the key // with the os // positive value is time in ms that the dispatcher // tries again (used to debounce) } }); } protected void injectKey(int keycode) { InputManager inputManager = (InputManager) XposedHelpers .callStaticMethod(InputManager.class, "getInstance"); long now = SystemClock.uptimeMillis(); final KeyEvent downEvent = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keycode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_KEYBOARD); final KeyEvent upEvent = KeyEvent.changeAction(downEvent, KeyEvent.ACTION_UP); Integer INJECT_INPUT_EVENT_MODE_ASYNC = XposedHelpers .getStaticIntField(InputManager.class, "INJECT_INPUT_EVENT_MODE_ASYNC"); XposedHelpers.callMethod(inputManager, "injectInputEvent", downEvent, INJECT_INPUT_EVENT_MODE_ASYNC); XposedHelpers.callMethod(inputManager, "injectInputEvent", upEvent, INJECT_INPUT_EVENT_MODE_ASYNC); } }